/* ===================

AX2E (AXESCON XACML 2.0 Engine) is the Java authorization engine, which implements OASIS XACML 2.0 standard.
Copyright (C) 2007 AXESCON LLC

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA


Contact: AXESCON LLC - info{at}axescon.com

 =================== */
package axescon.xacml.engine;

import axescon.xacml.api.*;
import axescon.xacml.model.ctx.XacmlCtx;
import axescon.xacml.model.policy.IdReference;
import axescon.xacml.model.policy.PolicyObject;

import java.util.*;
import java.util.logging.Logger;

/**
 * @author argyn
 *         Date: Oct 6, 2005
 *         Responsibilities: knows how to find policy matching request and
 *         how to load a policy by reference
 */
public class BasePolicyStore implements PolicyReferenceStore, PolicyStore {
    Logger log = Logger.getLogger(getClass().getName());

    /**
     * contains a list of policie objects, to be target matched agains the given req
     * List<PolicyObject>
     */
    private List policyCache = new ArrayList();


    /**
     * contains a map with IdReference key, and value is
     * another map with version as a key and value is policy object
     * Map<String, Map<String, PolicyObject>>
     */
    private Map policyRefCache = new TreeMap();

    /**
     * find a policy(s), which (target) matches a given request.
     * there can be more than one found. t's up to a caller to decide what to
     * do with more than one policy matching.
     *
     * @param req
     * @param ch
     * @return List<PolicyObject>
     */
    public List find(XacmlCtx req, CtxHandler ch, TargetMatcher tm) throws TargetMatchIndeterminateException,
            XacmlException {
        ArrayList ret = null;
        Iterator it = policyCache.iterator();
        while (it.hasNext()) {
            PolicyObject p = (PolicyObject) it.next();
            if (tm.match(p.getTarget(), req, ch)) {
                if (ret == null) ret = new ArrayList(1);
                ret.add(p);
            }
        }

        return ret;
    }

    /**
     * loads a policy by Id
     *
     * @param idRef
     * @return
     */
    public PolicyObject resolve(IdReference idRef) throws XacmlException {
        //this can only be a processing problem in engine code
        assert idRef != null: "Null IdReference passed to BasePolicyStore.";

        //this can only be a processing problem in engine code
        assert idRef.getId() != null: "BasePolicyStore: id is missing in id reference object.";

        Map vmap = (Map) policyRefCache.get(idRef.getId());
        if (vmap != null && vmap.size() > 0) {
            PolicyObject ret = null;
            Iterator it = vmap.values().iterator();
            while (it.hasNext()) {
                PolicyObject p = (PolicyObject) it.next();
                if (!idRef.match(p.getId())) continue;
                if (ret == null)
                    ret = p;
                else if (p.getVersion().compareTo(ret.getVersion()) > 0) ret = p;
            }
            if (ret == null)
                throw new UnresolvedIdReferenceException("BasePolicyStore found a policy/set id:" + idRef.getId()
                        + ", but versions dont match",
                        XacmlException.ERR_CODE_PROCESSING_ID_REFERENCE_VERSION_MATCH);
            else
                return ret;

        } else
            throw new UnresolvedIdReferenceException("BasePolicyStore cant find a policy/set id:" + idRef.getId(),
                    XacmlException.ERR_CODE_PROCESSING_ID_REFERENCE_NOT_FOUND);
    }

    public void addPolicyToReferenceCache(PolicyObject p) {
        if (p == null) return;
        String idRef = p.getId();
        Map vmap = (Map) policyRefCache.get(idRef);
        if (vmap == null) {
            vmap = new TreeMap();
            policyRefCache.put(idRef, vmap);
        }
        String v = p.getVersion();
        vmap.put(v, p);
    }

    public void addPolicyToStore(PolicyObject p) {
        if (p == null) return;
        policyCache.add(p);
    }


    /**
     * @return List<PolicyObject>
     */
    public List getPolicyCache() {
        return policyCache;
    }

    /**
     * List<PolicyObject>
     *
     * @param policyCache
     */
    public void setPolicyCache(List policyCache) {
        this.policyCache = policyCache;
    }

    /**
     * @return Map<String, Map<String, PolicyObject>>
     */
    public Map getPolicyRefCache() {
        return policyRefCache;
    }

    /**
     * @param policyRefCache Map<String, Map<String, PolicyObject>>
     */
    public void setPolicyRefCache(Map policyRefCache) {
        this.policyRefCache = policyRefCache;
    }

    public void addPolicy(PolicyObject p) {
        addPolicyToReferenceCache(p);
        addPolicyToStore(p);
    }

    private PolicyFactory policyFactory;

    public PolicyFactory getPolicyFactory() {
        return policyFactory;
    }

    public void setPolicyFactory(PolicyFactory policyFactory) {
        this.policyFactory = policyFactory;
    }

    public void validate(PolicyEvaluator pe) throws XacmlException {
        Iterator it= getPolicyCache().iterator();
        String polId="unknown";
        try {
            while(it.hasNext()){
                PolicyObject po= (PolicyObject)it.next();
                po.validate(pe);
                log.config(getClass().getName()+" successfully validated policy "+(polId=po.getId()));
            }

            it= getPolicyRefCache().values().iterator();
            while(it.hasNext()){
                Iterator it1= ((Map)it.next()).values().iterator();
                while(it1.hasNext()){
                    PolicyObject po= (PolicyObject)it1.next();
                    if(getPolicyCache().contains(po))continue;
                    po.validate(pe);
                    log.config(getClass().getName()+" successfully validated policy "+(polId=po.getId()));
                }
            }
        } catch (XacmlException e) {
            log.config(getClass().getName()+" failed to validate policy "+polId);
            throw e;
        }

    }
}
